<?php

namespace Factity;

class AuthActionsTest extends TestCase {
    
    public function testGeneral() {
        $methodCount = 14;
        self::assertClassExists("Factity\AuthActions");
        $class = new \ReflectionClass("Factity\AuthActions");
        $this->assertPublicProtectedMethodCount($class, $methodCount);
    }
    
    /**
     * Initializes all required non mock objects
     * @return unknown_type
     */
    public function initFactityForTests() {
        $this->getTestLoader()->initFattenFactity(); // will create BlueCollar and DataBrain
        $authActions = new AuthActions();
        $blueCollar = Factity::getInstance()->getBlueCollar();
        $blueCollar->setAuthActions($authActions);
        $authActions->setBlueCollar($blueCollar);
    }
    
    /**
     * @depends testGeneral
     * @test
     */
    public function executeAutologin() {
        $this->initFactityForTests();
        $action = Factity::getInstance()->getBlueCollar()->getAuthActions();
        
        $this->assertMethodExists($action, "executeAutologin");
        $this->assertMethodCanBeInvoked($action, "executeAutologin", 1);
        $autReq = new AutologinRequestData();
        $autoCookieHelper = new AutoCookieHelper();
        $autoCookieHelper->setAutoCookieValidDuration('+1 month');
        $pcookie = new PureAutoCookieData();
        $pcookie->setToken("a");
        $pcookie->setUid("2");
        $pcookie->setValidTill(date("r", strtotime("+1 day")));
        $pcookie->setSeriesId("b");
        //generate cookie
        $autReq->setAutoCookie($autoCookieHelper->createAutoCookie($pcookie)->getCookieText());
        $userdata = new UserData();
        $userdata->setUid("2");
        $body = new LoginResponseBody();
        $responseMock = $this->getMock("Factity\GeneralResponse", array(
            "setBody"
        ));
        $userConvMock = $this->getMock("Factity\GeneralUserConv", array(
            "getUserByUid"
        ));
        $autoConvMock = $this->getMock("Factity\GeneralAutorememberConv", array(
            "getAutoCookie",
            "cleanUserAutoCookies",
            "setNewAutoCookie"
        ));
        $action->getDataBrain()->setUserConv($userConvMock);
        $action->getDataBrain()->setAutorememberConv($autoConvMock);
        $action->setResponse($responseMock);
        $userConvMock->expects($this->any())->method("getUserByUid")->with($pcookie->getUid())->will($this->returnValue($userdata));
        $autoConvMock->expects($this->once())->method("getAutoCookie")->will($this->returnValue($pcookie));
        $body->setUser($userdata);
        $body->setAutoCookie($pcookie);
        $action->executeAutoLogin($autReq);
        //check if exception is thrown when autocookie is not found
        $autoConvMock->expects($this->any())->method("getAutoCookie");
        $pcookie = new PureAutoCookieData();
        $pcookie->setToken("a");
        $pcookie->setUid("2");
        $pcookie->setValidTill(date("r", strtotime("-1 day")));
        $pcookie->setSeriesId("b");
        //check if exception is thrown when autocookie is too old
        $autReq->setAutoCookie($autoCookieHelper->createAutoCookie($pcookie)->getCookieText());
        $autoConvMock = $this->getMock("Factity\GeneralAutorememberConv", array(
            "getAutoCookie",
            "cleanUserAutoCookies",
            "setNewAutoCookie"
        ));
        $autoConvMock->expects($this->once())->method("getAutoCookie")->will($this->returnValue($pcookie));
        $action->getDataBrain()->setAutorememberConv($autoConvMock);
        $exceptionThrown = false;
        try {
            $action->executeAutoLogin($autReq);
        }
        catch(Exception $ex) {
            $this->assertType("Factity\AutoCookieTooOldException", $ex, "Wrong exception is thrown when link is too old");
            $exceptionThrown = true;
        }
        $this->assertTrue($exceptionThrown, "Exception was not thrown, although expected!");
        //check if exception is thrown when autocookie is not found
        $autoConvMock = $this->getMock("Factity\GeneralAutorememberConv", array(
            "getAutoCookie",
            "cleanUserAutoCookies",
            "setNewAutoCookie"
        ));
        $autoConvMock->expects($this->once())->method("getAutoCookie")->will($this->returnValue(null));
        $action->getDataBrain()->setAutorememberConv($autoConvMock);
        $exceptionThrown = false;
        try {
            $action->executeAutoLogin($autReq);
        }
        catch(Exception $ex) {
            $this->assertType("Factity\TripletAutoCookieDoesNotExistException", $ex, "Wrong exception is thrown when link is too old");
            $exceptionThrown = true;
        }
        $this->assertTrue($exceptionThrown, "Exception was not thrown, although expected!");
    }
    
    private function setUpForExecuteLogin() {
        $this->initFactityForTests();
        $factity = Factity::getInstance();
        $blueCollar = $factity->getBlueCollar();
        $action = $blueCollar->getAuthActions();
        $dataBrain = $factity->getDataBrain();
        $this->assertMethodExists($action, "executeLogin");
        $this->assertMethodCanBeInvoked($action, "executeLogin", 1);
        
        // setup autoremember conv
        $autoMockConv = $this->getMock('Factity\GeneralAutorememberConv');
        $dataBrain->setAutorememberConv($autoMockConv);
        // setup user conv
        $userMockConv = $this->getMock('Factity\GeneralUserConv');
        $dataBrain->setUserConv($userMockConv);
        // setu password helper
        $passwordMockHelper = $this->getMock('Factity\PasswordHelper');
        $blueCollar->setPasswordHelper($passwordMockHelper);
        // auto mock helper
        $autoMockHelper = $this->getMock('Factity\AutoCookieHelper');
        $blueCollar->setAutoCookieHelper($autoMockHelper);
        // response
        $responseMock = $this->getMock('Factity\GeneralResponse');
        $action->setResponse($responseMock);
    }
    
    /**
     * @depends testGeneral
     * @test
     */
    public function executeLoginCheckValidation() {
        $this->setUpForExecuteLogin();
        $factity = Factity::getInstance();
        $blueCollar = $factity->getBlueCollar();
        $action = $blueCollar->getAuthActions();
        
        $req = new LoginRequestData();

        try {
            $action->executeLogin($req);
            $this->fail('Validation exception has not been thrown');
        } catch(Exception $ex) {
            $this->assertType("Factity\ValidationException", $ex, 'Email is not passed');
        }
        
        $req->setEmail('notempty@mail.ru');
        $req->setPassword('');
        try {
            $action->executeLogin($req);
            $this->fail('Validation exception has not been thrown');
        } catch(Exception $ex) {
            $this->assertType("Factity\ValidationException", $ex, 'Password is not passed');
        }
    }
    
    /**
     * @depends executeLoginCheckValidation
     * @test
     */
    public function executeLoginInvalidPassword() {
        $this->setUpForExecuteLogin();
        $factity = Factity::getInstance();
        $blueCollar = $factity->getBlueCollar();
        $action = $blueCollar->getAuthActions();
        $dataBrain = $factity->getDataBrain();
        $userMockConv = $dataBrain->getUserConv();
        $autoMockConv = $dataBrain->getAutorememberConv();
        $passMockHelper = $blueCollar->getPasswordHelper();
        $autoMockHelper = $blueCollar->getAutoCookieHelper();
        
        $password = 'password';
        $email = 'email@mail.ru';
        $req = new LoginRequestData();
        $req->setPassword($password);
        $req->setEmail($email);
        
        // retrieve user from db
        $userDataResult = new UserData();
        $userDataResult->setUid('1');
        $userDataResult->setPassword('hashed password');
        
        $userMockConv->expects($this->atLeastOnce())->method('getUserByEmail')->with($email)->will($this->returnValue($userDataResult));
        $passMockHelper->expects($this->atLeastOnce())->method('hashPassword')->with($password)->will($this->returnValue('different hashed password'));
        
        try {
            $action->executeLogin($req); 
            $this->fail('Different Password exception has not been thrown');
        } catch(Exception $ex) {
            $this->assertType("Factity\PasswordMismatchException", $ex, 'Hashed passwords are different');
        }
    } 

    /**
     * @depends executeLoginCheckValidation
     * @test
     */
    public function executeLoginValid() {
        $this->setUpForExecuteLogin();
        $factity = Factity::getInstance();
        $blueCollar = $factity->getBlueCollar();
        $action = $blueCollar->getAuthActions();
        $responseMock = $action->getResponse();
        $dataBrain = $factity->getDataBrain();
        $userMockConv = $dataBrain->getUserConv();
        $autoMockConv = $dataBrain->getAutorememberConv();
        $passMockHelper = $blueCollar->getPasswordHelper();
        $autoMockHelper = $blueCollar->getAutoCookieHelper();
        
        $password = 'password';
        $email = 'email@mail.ru';
        $req = new LoginRequestData();
        $req->setPassword($password);
        $req->setEmail($email);
        
        // retrieve user from db
        $userDataResult = new UserData();
        $userDataResult->setUid('1');
        $userDataResult->setPassword('hashed password');
        
        $userMockConv->expects($this->atLeastOnce())->method('getUserByEmail')->with($email)->will($this->returnValue($userDataResult));
        $passMockHelper->expects($this->atLeastOnce())->method('hashPassword')->with($password)->will($this->returnValue('hashed password'));

        $pureAutoCookie = new PureAutoCookieData();
        $autoMockHelper->expects($this->atLeastOnce())->method('createPureAutoCookie')->with($userDataResult)->will($this->returnValue($pureAutoCookie));
        $autoMockConv->expects($this->atLeastOnce())->method('saveNewAutoCookie')->with($pureAutoCookie);
        $autoMockHelper->expects($this->atLeastOnce())->method('createAutoCookie')->with($this->equalTo($pureAutoCookie));
        $responseMock->expects($this->atLeastOnce())->method('setBody');
        
        $action->executeLogin($req); 
    }     
  
    private function setUpForExecuteLogout() {
        $this->initFactityForTests();
        $factity = Factity::getInstance();
        $blueCollar = $factity->getBlueCollar();
        $action = $blueCollar->getAuthActions();
        $dataBrain = $factity->getDataBrain();
        $this->assertMethodExists($action, "executeLogout");
        $this->assertMethodCanBeInvoked($action, "executeLogout", 1);
        
        // setup autoremember conv
        $autoMockConv = $this->getMock('Factity\GeneralAutorememberConv');
        $dataBrain->setAutorememberConv($autoMockConv);
        // auto mock helper
        $autoMockHelper = $this->getMock('Factity\AutoCookieHelper');
        $blueCollar->setAutoCookieHelper($autoMockHelper);
        // response
        $responseMock = $this->getMock('Factity\GeneralResponse');
        $action->setResponse($responseMock);
    }
    
    /**
     * @depends testGeneral
     * @test
     */
    public function executeLogoutValidation() {
        $this->setUpForExecuteLogout();

        $factity = Factity::getInstance();
        $blueCollar = $factity->getBlueCollar();
        $action = $blueCollar->getAuthActions();
        
        $req = new LogoutRequestData();

        try {
            $action->executeLogout($req);
            $this->fail('Validation exception has not been thrown');
        } catch(Exception $ex) {
            $this->assertType('Factity\ValidationException', $ex);
        }
    }
    
    /**
     * @depends executeLogoutValidation
     * @test
     */
    public function executeLogoutValid() {
        $this->setUpForExecuteLogout();
        $factity = Factity::getInstance();
        $dataBrain = $factity->getDataBrain();
        $blueCollar = $factity->getBlueCollar();
        $action = $blueCollar->getAuthActions();
        $responseMock = $action->getResponse();
        $autoMockConv = $dataBrain->getAutorememberConv();
        $autoMockHelper = $blueCollar->getAutoCookieHelper();
        
        $req = new LogoutRequestData();
        $autoCookieText = 'auto cookie';
        $pureAutoCookieData = new PureAutoCookieData();
        $req->setAutoCookie($autoCookieText);
        
        $autoMockHelper->expects($this->atLeastOnce())->method('decodeAutoCookieTextToData')->with($autoCookieText)->will($this->returnValue($pureAutoCookieData));
        $autoMockConv->expects($this->atLeastOnce())->method('deleteAutoCookie')->with($pureAutoCookieData);
        $responseMock->expects($this->atLeastOnce())->method('setBody');
        
        $action->executeLogout($req);
        
    }
}
